Skip to content

Adopt go-opentype v0.5.0 subsetting + descriptor primitives (real CFF subsetting) - #1

Merged
tannevaled merged 1 commit into
mainfrom
feat/opentype-v0.5-subsetting
Jul 28, 2026
Merged

Adopt go-opentype v0.5.0 subsetting + descriptor primitives (real CFF subsetting)#1
tannevaled merged 1 commit into
mainfrom
feat/opentype-v0.5-subsetting

Conversation

@tannevaled

Copy link
Copy Markdown
Contributor

Summary

Refactors font embedding onto go-opentype v0.5.0, deleting pdfkit's private sfnt re-parse and glyf subsetter and gaining real CFF charstring subsetting (previously the whole CFF table was embedded).

Deleted: sfnt.go, subset.go (and their tests). All sfnt parsing, subsetting and descriptor metrics now come from go-opentype.

What now uses which upstream primitive

  • FontDescriptorFontBBox, Ascent/Descent, CapHeight (with the documented cap-height→ascender fallback), ItalicAngle, Flags, StemV, UnitsPerEm read directly from *opentype.Font.
  • TrueType (CIDFontType2/FontFile2) — embeds Font.SubsetTrueType output (compact renumbered subset) and builds a /CIDToGIDMap stream from the returned old→new remap (CID = original glyph id → subset glyph id). Content-stream glyph ids and /W stay keyed by the original id (the CID), which the map resolves.
  • CFF (CIDFontType0/FontFile3) — now subsets charstrings via Font.SubsetCFF (glyph numbering preserved → Identity /CIDToGIDMap stays valid).
  • /W widths — from Face.AdvanceIndexUnits.

CFF is now truly subsetted (proof)

For "Hello" from Source Serif 4: whole CFF = 115354 bytes, embedded subset = 45512 bytes (39.5%). The oracle test re-opens the PDF with rsc.io/pdf, asserts the embedded program is smaller than the whole table, wraps it back into an OTF, re-parses with go-opentype and checks every kept glyph renders contour-identically. The TrueType oracle resolves each drawn glyph through the embedded /CIDToGIDMap and checks it contour-intact.

Graceful fallback

A CID-keyed CFF or CFF2 (variable) font (which the preserve-numbering subsetter rejects) falls back to embedding the whole CFF /CFF2 table; a TrueType subset failure falls back to the whole font program. Both fallbacks are covered by tests (a synthesised CFF2 font and an out-of-range-glyph trigger).

Gate

  • GOWORK=off CGO_ENABLED=0, pure-Go, go 1.26.4
  • Exact 100% statement coverage
  • go vet clean; builds on 6 64-bit arches (amd64/arm64/riscv64/loong64/ppc64le/s390x) + wasm (+ darwin/windows)

🤖 Generated with Claude Code

Delete pdfkit's private sfnt re-parse (sfnt.go) and TrueType glyf
subsetter (subset.go); embedding now uses go-opentype v0.5.0 throughout:

- FontDescriptor scalars (FontBBox, Ascent/Descent, CapHeight, ItalicAngle,
  Flags, StemV, UnitsPerEm) come straight from the new Font accessors, with
  the documented cap-height -> ascender fallback.
- TrueType path embeds Font.SubsetTrueType output (a compact renumbered
  subset) and builds a /CIDToGIDMap stream from the returned old->new remap
  (CID = original glyph id -> subset glyph id).
- CFF path now truly subsets charstrings via Font.SubsetCFF instead of
  embedding the whole 'CFF ' table (glyph numbering preserved, Identity
  CIDToGIDMap stays valid). A CID-keyed CFF or a CFF2 font, which the
  preserve-numbering subsetter rejects, falls back to embedding the whole
  'CFF '/'CFF2' table; a TrueType subset failure falls back to the whole
  font program.
- /W widths now come from Face.AdvanceIndexUnits.

Oracle tests re-open the generated PDF with rsc.io/pdf and prove the CFF
subset is smaller than the whole table and re-parses with every kept glyph
contour-intact; the TrueType subset is resolved through the embedded
/CIDToGIDMap and each drawn glyph checked contour-intact. Exact 100%
statement coverage; go vet clean; builds on 6 64-bit arches + wasm.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@tannevaled
tannevaled merged commit 9afcac8 into main Jul 28, 2026
1 check passed
@tannevaled
tannevaled deleted the feat/opentype-v0.5-subsetting branch July 28, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant